package sz.com.battleship.game.GameElement;

import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

public class GameBoard implements Serializable
{
	/**
	 * 
	 */
	private static final long serialVersionUID = -1960609456066790743L;

	public static final int BOARD_SIZE = 100;
	
	//Amount of required boat per boat type and boattype size
	public static final int vikingAmount = 2;
	public static final int phoenixAmount = 1;
	public static final int medivacAmount = 1;
	public static final int carrierAmount = 2;
	public static final int battlecruiserAmount = 1; 
	 
	private HashMap<Integer,Ship> boardMap_; //Mapping all occupied map position to appropriate ship
	private boolean hitList[] = new boolean[BOARD_SIZE]; //Hit list mapping all position which have been fired upon (false=non fired upon, true=fired upon)
	
	public GameBoard()
	{
		boardMap_ = new HashMap<Integer,Ship>();
	}
	
	//Returns
	// -1 if position has already been fired upon
	// 0 if no ship was present at position
	// 1 if ship was hitted but not sinked
	// 2 if ship was sinked
	public int receiveFire(int position)
	{
		if (hitList[position])
		{
			if (boardWinState())
			{
				return 3;
			}
			return -1;
		}
		else
		{
			hitList[position] = true; //setting position as having been fired upon
			if (hasShip(position))
			{
				if (getShip(position).takeHits())
				{
					if (boardWinState())
					{
						return 3;
					}
					return 2;
				}
				else
				{
					if (boardWinState())
					{
						return 3;
					}
					return 1;
				}
			}
			else
			{
				if (boardWinState())
				{
					return 3;
				}
				return 0;
			}
		}
	}
	
	//Returns true if all ship have been sinked
	public boolean boardWinState()
	{
		Set<Ship> uniqueShips = new HashSet<Ship>(boardMap_.values());
		for(Ship current : uniqueShips)
		{
			if (current.getHits() > 0)
			{
				return false;
			}
		}
		return true;
	}
	//Return true if a ship or part of a ship is present at position
	public boolean hasShip(int position)
	{
		if (boardMap_.containsKey(position))
		{
			return true;
		}
		return false;
	}
	
	//Verify if the amount of ship of Type type is not already maxed, return true if ships of Type type can still be added to the board
	public boolean shipCanBeAdded(int type)
	{
		int count = 0;
		boolean isAddable = false;
		Set<Ship> uniqueShips = new HashSet<Ship>(boardMap_.values()); //to remove duplicate ship when using hashmap.values()
		for(Ship current : uniqueShips)
		{
			if (current.getType() == type)
			{
				count++;
			}
		}
		switch(type)
		{
		case 0:
			if (count < battlecruiserAmount)
			{
				isAddable = true; 
			}
			break;
		case 1:
			if (count < carrierAmount)
			{
				isAddable = true; 
			}
			break;
		case 2:
			if (count < medivacAmount)
			{
				isAddable = true; 
			}
			break;
		case 3:
			if (count < phoenixAmount)
			{
				isAddable = true; 
			}
			break;
		case 4:
			if (count < vikingAmount)
			{
				isAddable = true; 
			}
			break;
		}
		return isAddable;
	}
	
	//If the appropriate ship can be placed at these position, the appropriate ship is created and mapped in the boardmap and board
	public boolean placeShip(int position1, int position2, int type)
	{
		if (!this.shipCanBeAdded(type))
		{
			return false;
		}
		if (position1 < position2)
		{ //ship is either placed from left to right or top to bottom 
			if ((position1 / 10) == (position2 / 10))
			{ //ship is placed on row from left to right
				if (((position2 - position1) + 1) == Ship.getSize(type))
				{ //Position makes for a ship of the appropriate size
					if (verifyRow(position1,position2))
					{
						addShipRow(position1,position2,type);
						return true;
					}
				}
				return false;
			}
			else if ((position2 % 10) == (position1 % 10)) //making sure ship is not diagonal
			{ //Ship is placed vertically on column from top to bottom
				if (((position2 / 10 - position1 / 10) + 1) == Ship.getSize(type))
				{ //Position makes for a ship of the appropriate size
					if (verifyColumn(position1,position2))
					{
						addShipColumn(position1,position2,type);
						return true;
					}
				}
				return false;
			}
			return false;
		}
		else if (position1 > position2)
		{ //ship is either placed from right to left or bottom to top
			if ((position1 / 10) == (position2 / 10))
			{ //ship is placed on row from left to right
				if (((position1 - position2) + 1) == Ship.getSize(type))
				{ //Position makes for a ship of the appropriate size
					if (verifyRow(position1,position2))
					{
						addShipRow(position1,position2,type);
						return true;
					}
				}
				return false;
			}
			else if ((position1 % 10) == (position2 % 10)) //making sure ship is not diagonal
			{ //Ship is placed vertically on column from top to bottom
				if (((position1 / 10 - position2 / 10) + 1) == Ship.getSize(type))
				{ //Position makes for a ship of the appropriate size
					if (verifyColumn(position1,position2))
					{
						addShipColumn(position1,position2,type);
						return true;
					}
				}
				return false; 
			}
			return false;			
		}
		return false; //impossible positions attempt
	}
	
	public void clearBoard()
	{
		boardMap_.clear();
	}
	public Ship getShip(int position)
	{
		return boardMap_.get(position); //While return null if no ship is present at position
	}
	
	private void addShipRow(int position1, int position2, int type)
	{
		HashSet<Integer> boardSpaces = new HashSet<Integer>();
		if (position1 < position2)
		{
			for(int i = position1; i <= position2; i++)
			{
				boardSpaces.add(i);
			}
			
			//Creating new ship with just created boardSpace
			Ship ship = new Ship(type, boardSpaces);
			for(Integer current : boardSpaces)
			{ 
				boardMap_.put(current, ship);
			}
		}
		else if(position1 > position2)
		{
			for(int i = position2; i <= position1; i++)
			{
				boardSpaces.add(i);
			}
			
			//Creating new ship with just created boardSpace
			Ship ship = new Ship(type, boardSpaces);
			for(Integer current : boardSpaces)
			{ 
				boardMap_.put(current, ship);
			}
		}	
	}
	
	private void addShipColumn(int position1, int position2, int type)
	{
		HashSet<Integer> boardSpaces = new HashSet<Integer>();
		if (position1 < position2)
		{
			for(int i = position1; i <= position2; i += 10)
			{
				boardSpaces.add(i);
			}
			
			//Creating new ship with just created boardSpace
			Ship ship = new Ship(type, boardSpaces);
			for(Integer current : boardSpaces)
			{ 
				boardMap_.put(current, ship);
			}
		}
		else if(position1 > position2)
		{
			for(int i = position2; i <= position1; i += 10)
			{
				boardSpaces.add(i);
			}
			
			//Creating new ship with just created boardSpace
			Ship ship = new Ship(type, boardSpaces);
			for(Integer current : boardSpaces)
			{ 
				boardMap_.put(current, ship);
			}
		}	
	}
	
	//returns true if a ship is already present in the row
	private boolean verifyRow(int position1, int position2)
	{
		if (position1 < position2)
		{
			for(int i = position1; i <= position2; i++)
			{
				if (boardMap_.containsKey(i)) //there is a boat at i
				{ //position is occupied
					return false; 
				}
			}
			return true; 
		}
		else if(position1 > position2)
		{
			for(int i = position2; i <= position1; i++)
			{
				if (boardMap_.containsKey(i)) //there is a boat at i
				{ //position is occupied
					return false; 
				}
			}
			return true; 
		}
		return false;
	}
	
	//return true if a ship is already present in the column
	private boolean verifyColumn(int position1, int position2)
	{
		if (position1 < position2)
		{
			for(int i = position1; i <= position2; i += 10)
			{
				if (boardMap_.containsKey(i)) //there is a boat at i
				{ //position is occupied
					return false; 
				}
			}
			return true; 
		}
		else if(position1 > position2)
		{
			for(int i = position2; i <= position1; i += 10)
			{
				if (boardMap_.containsKey(i)) //there is a boat at i
				{ //position is occupied
					return false; 
				}
			}
			return true; 
		}
		return false;		
	}
	
	public boolean removeShip(int position)
	{
		//todo
		return false;
	}
}
